package netective.gui;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;

import jpcap.NetworkInterfaceAddress;
import netective.api.gatherer.DeviceInterface;
import netective.engine.Engine;

@SuppressWarnings("serial")
public class SettingsDevicesWindow extends JFrame {
	private static final int WINDOW_WIDTH = 800;
	private static final int WINDOW_HEIGHT = 240;
	private static final String APP_TITLE = "Devices Settings NETECTIVE";

	private DeviceInterface[] devices;
	private Engine engine;

	public DeviceInterface[] getDevices() {
		return devices;
	}

	public void setDevices(DeviceInterface[] devices) {
		this.devices = devices;
	}

	public SettingsDevicesWindow(Engine engine) {
		this.engine = engine;
		setDevices(engine.getManager().getDevices());

		this.setSize(new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT));
		this.setTitle(APP_TITLE);
		this.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);

		buildTable();

	//	this.setVisible(true);
	}

	public void buildTable() {
		JTable table = new JTable(new DevicesTableModel(devices));

		TableCellRenderer defaultRenderer;
		defaultRenderer = table.getDefaultRenderer(JButton.class);
		table.setDefaultRenderer(JButton.class, new DevicesCellRenderer(
				defaultRenderer));

		//TableColumnModel tcm = table.getColumnModel();
		//tcm.getColumn(0).setMinWidth(250);

		//table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
		table.setPreferredScrollableViewportSize(table
				.getPreferredScrollableViewportSize());
		table.addMouseListener(new JTableButtonMouseListener(table, engine));

		JScrollPane scrollPane = new JScrollPane(table);
		this.getContentPane().add(scrollPane, "Center");
		this.pack();
	}
}

class DevicesCellRenderer implements TableCellRenderer {
	private TableCellRenderer __defaultRenderer;

	public DevicesCellRenderer(TableCellRenderer renderer) {
		__defaultRenderer = renderer;
	}

	public Component getTableCellRendererComponent(JTable table, Object value,
			boolean isSelected, boolean hasFocus, int row, int column) {
		if (value instanceof Component) {
			return (Component) value;
		}
		return __defaultRenderer.getTableCellRendererComponent(table, value,
				isSelected, hasFocus, row, column);
	}

}

/**
 * Devices Table Model.
 * 
 */
@SuppressWarnings("serial")
class DevicesTableModel extends AbstractTableModel {
	protected String[] columnNames = { "Name", "Type", "MAC address",
			"IP address", "State", "Action" };
	private Object[][] data;
	private static int COLUMN_COUNT = 6;
	private static final int STATE_COLUMN = 4;
	private static final int ACTION_COLUMN = 5;

	DevicesTableModel(DeviceInterface[] devices) {
		setData(devices);
	}

	private void setData(DeviceInterface[] devices) {
		data = new Object[devices.length][COLUMN_COUNT];
		for (int i = 0; i < devices.length; i++) {
			data[i][0] = devices[i].description;
			data[i][1] = devices[i].datalink_description;

			String mac = "";
			for (byte b : devices[i].mac_address) {
				mac += Integer.toHexString(b & 0xff) + ":";
			}
			data[i][2] = mac;

			String ip = "";
			for (NetworkInterfaceAddress a : devices[i].addresses) {
				ip += a.address + " " + a.subnet + " " + a.broadcast;
			}
			data[i][3] = ip;
			data[i][4] = devices[i].stateToString();
			data[i][5] = new JButton("TURN ON");
		}
	}

	public Class<?> getColumnClass(int columnIndex) {
		return (data[0][columnIndex]).getClass();
	}

	public int getColumnCount() {
		return COLUMN_COUNT;
	}

	public String getColumnName(int columnIndex) {
		return columnNames[columnIndex];
	}

	public int getRowCount() {
		return data.length;
	}

	public Object getValueAt(int rowIndex, int columnIndex) {
		return data[rowIndex][columnIndex];
	}

	@Override
	public boolean isCellEditable(int rowIndex, int columnIndex) {
		return false;
	}

	@Override
	public void removeTableModelListener(TableModelListener l) {
		// TODO Auto-generated method stub

	}

	public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
		data[rowIndex][columnIndex] = aValue;
	}
	
	public void changeState(int rowIndex) {
		turnOffAllDevices(rowIndex);
	}
	
	public void setState(int rowIndex, boolean state) {
		if (state) {
			setValueAt("OFF", rowIndex, STATE_COLUMN);
			setValueAt(new JButton("TURN ON"), rowIndex, ACTION_COLUMN);
		} else {
			setValueAt("ON", rowIndex, STATE_COLUMN);
			setValueAt(new JButton("TURN OFF"), rowIndex, ACTION_COLUMN);
		}
	}
	
	public boolean isDeviceOn(int rowIndex)
	{
		return data[rowIndex][STATE_COLUMN].equals("ON");
	}
	
    public void turnOffAllDevices()
    {
    	turnOffAllDevices(-1);
    }
	
	/**
	 * Changing state of all devices except defined by parameter.
	 * It's only changing state in table!!!
	 * @param without device which state is not changing
	 */
	public void turnOffAllDevices(int without) {
		for (int i = 0; i < getRowCount(); i++) {
			if (i != without) {
				setState(i, true);
			} else {
				setState(i, false);
			}
		}
	}
}

class JTableButtonMouseListener implements MouseListener {
	private JTable table;
	private Engine engine;

	private void forwardEventToButton(MouseEvent e) {
		TableColumnModel columnModel = table.getColumnModel();
		int column = columnModel.getColumnIndexAtX(e.getX());
		int row = e.getY() / table.getRowHeight();
		Object value;
		JButton button;
		MouseEvent buttonEvent;
		if (row >= table.getRowCount() || row < 0
				|| column >= table.getColumnCount() || column < 0)
			return;
		value = table.getValueAt(row, column);
		if (!(value instanceof JButton))
			return;
		button = (JButton) value;
		buttonEvent = (MouseEvent) SwingUtilities.convertMouseEvent(table, e,
				button);
		button.dispatchEvent(buttonEvent);
		
		DevicesTableModel t = (DevicesTableModel)table.getModel();
		
		if (!t.isDeviceOn(row) &&
			engine.openDevice(row)) {
			//System.out.println(row);
			t.changeState(row);
		}
		else
		{
			t.turnOffAllDevices();
			engine.getManager().setDeviceChanged(true);
		}
		
		//System.out.println(table.getValueAt(row,  4));
		
		// This is necessary so that when a button is pressed and released
		// it gets rendered properly. Otherwise, the button may still appear
		// pressed down when it has been released.
		table.repaint();
	}

	public JTableButtonMouseListener(JTable table, Engine engine) {
		this.table = table;
		this.engine = engine;
	}

	public void mouseClicked(MouseEvent e) {
		forwardEventToButton(e);
	}

	public void mouseEntered(MouseEvent e) {
		//forwardEventToButton(e);
	}

	public void mouseExited(MouseEvent e) {
		//forwardEventToButton(e);
	}

	public void mousePressed(MouseEvent e) {
		//forwardEventToButton(e);
	}

	public void mouseReleased(MouseEvent e) {
		//forwardEventToButton(e);
	}
}
